#include "Vec3.hpp"
#include <array>
#include <cmath>
#include <iostream>
#include <sstream>

using std::ostream;
using std::sqrt;
using std::ostringstream;
using std::array;

namespace gmath
{

  template<class T>
    Vec3<T>::Vec3()
    {
      data_.fill(0);
    }

  template<class T>
    Vec3<T>::Vec3(T value)
    {
      setData(value);
    }

  template<class T>
    Vec3<T>::Vec3(T x, T y, T z)
    {
      setData(x, y, z);
    }

  template<class T>
      Vec3<T>::Vec3(T* ptr)
      {
        setData(ptr);
      }

  template<class T>
    const std::array<T, 3>& gmath::Vec3<T>::getData() const
    {
      return data_;
    }

  template<class T>
    void gmath::Vec3<T>::setData(const std::array<T, 3> data)
    {
      data_ = data;
    }

  template<class T>
    inline void gmath::Vec3<T>::setData(T value)
    {
      data_.fill(value);
    }

  template<class T>
    inline void gmath::Vec3<T>::setData(T x, T y, T z)
    {
      data_[0] = x;
      data_[1] = y;
      data_[2] = z;
    }

  template<class T>
    inline void gmath::Vec3<T>::setData(T* ptr)
    {
      for (short i = 0; i < 3; i++)
        data_[i] = ptr[i];
    }

  template<class T>
    T Vec3<T>::length()
    {
      return sqrt(
          data_[0] * data_[0] + data_[1] * data_[1] + data_[2] * data_[2]);
    }

  template<class T>
    T Vec3<T>::dot(Vec3<T>& v2)
    {
      return data_[0] * v2.data_[0] + data_[1] * v2.data_[1]
          + data_[2] * v2.data_[2];
    }

  template<class T>
    Vec3<T> Vec3<T>::cross(Vec3<T>& v2)
    {
      Vec3<T> result;
      result.data_[0] = data_[1] * v2.data_[2] - data_[2] * v2.data_[1];
      result.data_[1] = data_[2] * v2.data_[0] - data_[0] * v2.data_[2];
      result.data_[2] = data_[0] * v2.data_[1] - data_[1] * v2.data_[0];
      return result;
    }

  template<class T>
    std::string Vec3<T>::toString()
    {
      ostringstream ss;
      ss << "{" << data_[0] << ", " << data_[1] << ", " << data_[2] << "}";
      return ss.str();
    }

  template<typename T>
    std::ostream& operator<<(std::ostream& out, Vec3<T> v)
    {
      out << v.toString();
      return out;
    }

  template<class T>
    Vec3<T> Vec3<T>::operator+(Vec3<T>& v2)
    {
      return Vec3<T>(data_[0] + v2.data_[0], data_[1] + v2.data_[1],
          data_[2] + v2.data_[2]);
    }

  template<class T>
    Vec3<T> Vec3<T>::operator-(Vec3<T>& v2)
    {
      return Vec3<T>(data_[0] - v2.data_[0], data_[1] - v2.data_[1],
          data_[2] - v2.data_[2]);
    }

  template<class T>
    Vec3<T> Vec3<T>::operator*(T a)
    {
      return Vec3<T>(data_[0] * a, data_[1] * a, data_[2] * a);
    }

  template<class T>
    Vec3<T> Vec3<T>::operator/(T a)
    {
      return Vec3<T>(data_[0] / a, data_[1] / a, data_[2] / a);
    }

  template<class T>
    Vec3<T> Vec3<T>::operator+=(Vec3<T>& v2)
    {
      data_[0] += v2.data_[0];
      data_[1] += v2.data_[1];
      data_[2] += v2.data_[2];
      return *this;
    }

  template<class T>
    Vec3<T> Vec3<T>::operator-=(Vec3<T>& v2)
    {
      data_[0] -= v2.data_[0];
      data_[1] -= v2.data_[1];
      data_[2] -= v2.data_[2];
      return *this;
    }

  template<class T>
    Vec3<T> Vec3<T>::operator*=(T a)
    {
      data_[0] *= a;
      data_[1] *= a;
      data_[2] *= a;
      return *this;
    }

  template<class T>
    Vec3<T> Vec3<T>::operator/=(T a)
    {
      data_[0] /= a;
      data_[1] /= a;
      data_[2] /= a;
      return *this;
    }

  template ostream& operator<<(ostream& out, Vec3<float> v);
  template ostream& operator<<(ostream& out, Vec3<double> v);
  template ostream& operator<<(ostream& out, Vec3<int> v);
  template class Vec3<float> ;
  template class Vec3<double> ;
  template class Vec3<int> ;
}
